/*
 * FileTable.h
 *
 *  Created on: 27/06/2014
 *      Author: Vitor
 */

#ifndef FILETABLE_H_
#define FILETABLE_H_

#include <string>
#include <map>
#include <vector>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "FileSystemElement.h"
#include "Directory.h"
#include "FileDescriptor.h"
#include "File.h"
#include "Serializable.h"
#include "ByteQueue.h"

class FileTable {
private:
	Directory* filesystem_root;
	std::map<std::string, FileDescriptor*> file_table;

	static void portable_mkdir(std::string directory_path) {
		adapt_path(directory_path);
		std::string command = "mkdir ";
		command += directory_path;
		system(command.c_str());
	}

	static bool directory_exists(std::string path) {
#ifdef _WIN32
		struct _stat info;
		if ( _stat(path.c_str(), &info) != 0) {
			fprintf(stderr, "Nao consegui accessar %s.\n", path.c_str());
		} else if (info.st_mode & S_IFDIR) {
			return true;
		}
#else
		struct stat info;
		if (stat(path.c_str(), &info) != 0) {
			fprintf(stderr, "Nao consegui accessar %s.\n", path.c_str());
		} else if (info.st_mode & S_IFDIR) {
			return true;
		}
#endif
		return false;
	}

	void portable_rmdir(std::string directory_path) {
		adapt_path(directory_path);
		auto command = get_remove_dir();
		command += directory_path;
		system(command.c_str());
	}

	void portable_rmfile(std::string full_path) {
		adapt_path(full_path);
#ifdef _WIN32
		std::string command = "del ";
#else
		std::string command = "rm -rf ";
#endif
		command += full_path;
		system(command.c_str());
	}

	void portable_rename(std::string old_full_path, std::string new_full_path) {
		adapt_path(old_full_path);
		adapt_path(new_full_path);
#ifdef _WIN32
		std::string command = "rename ";
		command += old_full_path + " ";
		if (ends_with(new_full_path, "\\")) {
			new_full_path = new_full_path.substr(0,
					new_full_path.find_last_of("\\"));
		}
		new_full_path = new_full_path.substr(
				new_full_path.find_last_of("\\") + 1);
		command += new_full_path;
#else
		std::string command = "mv ";
		command += old_full_path + " ";
		command += new_full_path;
#endif
		system(command.c_str());
	}

	static void adapt_path(std::string& path) {
#ifdef _WIN32
		size_t slash_position;
		while ((slash_position = path.find("/")) != std::string::npos) {
			path.replace(slash_position, 1, "\\");
		}
#endif
	}

	std::string get_remove_dir() {
#ifdef _WIN32
		return "rmdir /S /Q ";
#else
		return "rm -rf ";
#endif
	}

	void remove_initial_slash(std::string& path) {
		if (path[0] == '/') {
			path.erase(path.begin());
		}
	}

	std::string parent_path(std::string full_path);
	bool ends_with(std::string full_string, std::string ending);

	static Directory* deserialize_directory(ByteQueue& bytes, Directory* parent,
			std::map<std::string, FileDescriptor*>& file_table);
	static FileDescriptor* deserialize_file_descriptor(ByteQueue& bytes,
			std::map<std::string, FileDescriptor*>& file_table);

public:
	FileTable();
	FileTable(Directory* directory,
			std::map<std::string, FileDescriptor*> file_table);
	virtual ~FileTable();

	std::map<std::string, FileDescriptor*>& get_file_table() {
		return file_table;
	}

	unsigned int quantidade_arquivos(unsigned int par) {
		unsigned int count = 0;

		for (auto it = file_table.begin(); it != file_table.end(); it++) {
			if (it->second->get_location() == par
					|| it->second->get_copy_location() == par) {
				count++;
			}
		}
		return count;
	}

	void make_directory(std::string full_path);
	void remove_directory(std::string full_path);
	void rename_directory(std::string full_path, std::string new_full_path);

	void add_file(FileDescriptor* new_file);
	void add_file(FileDescriptor* new_file, std::vector<char> file_contents);
	void remove_file(std::string full_path);
	void rename_file(std::string full_path, std::string new_full_path);

	FileSystemElement* get_element_from_path(std::string full_path);

	void print_all() {
		filesystem_root->print();
	}

	ByteQueue serialize();
	static FileTable* deserialize(ByteQueue& bytes);

	void get_file_count(std::map<unsigned int, int>&);
	unsigned int get_top_peer(std::map<unsigned int, int>&);
	unsigned int get_bottom_peer(std::map<unsigned int, int>&);
	unsigned int get_bottom_peer_wo_file(std::map<unsigned int, int>&,
			std::string file_name);
	std::string get_balancing_file(unsigned int, unsigned int);
	std::vector<std::string> get_lost_peer_files(unsigned int);
	void update_file_table(unsigned int, unsigned int, std::string);
	unsigned int get_alternate_file_location(std::string, unsigned int);
};

#endif /* FILETABLE_H_ */
